package com.ruoyi.kanban.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.kanban.domain.DeliveryOrder;
import com.ruoyi.kanban.domain.vo.*;
import com.ruoyi.kanban.mapper.DeliveryOrderMapper;
import com.ruoyi.kanban.service.IKanbanDashboardService;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.WeekFields;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class KanbanDashboardServiceImpl extends ServiceImpl<DeliveryOrderMapper, DeliveryOrder> implements IKanbanDashboardService {

    private void applyTimeRange(LambdaQueryWrapper<DeliveryOrder> w, Date startTime, Date endTime) {
        if (startTime != null) {
            w.ge(DeliveryOrder::getRequiredDeliveryTime, startTime);
        }
        if (endTime != null) {
            w.le(DeliveryOrder::getRequiredDeliveryTime, endTime);
        }
    }

    @Override
    public DashboardSummaryVO getReceiverSummary(Date startTime, Date endTime) {
        LambdaQueryWrapper<DeliveryOrder> base = new LambdaQueryWrapper<>();
        base.eq(DeliveryOrder::getDelFlag, 0);
        applyTimeRange(base, startTime, endTime);

        // 为计算率类指标，先获取相关订单列表
        List<DeliveryOrder> all = this.list(base);
        int total = all.size();
        int completed = (int) all.stream().filter(o -> Objects.equals(o.getStatus(), 30)).count();
        int cancel = (int) all.stream().filter(o -> Objects.equals(o.getStatus(), -10)).count();
        int exception = (int) all.stream().filter(o -> Objects.equals(o.getExceptionFlag(), 1)).count();

        List<DeliveryOrder> delivered = all.stream().filter(o -> Objects.equals(o.getStatus(), 30)).collect(Collectors.toList());
        int deliveredCount = delivered.size();

        int onTimeCount = (int) delivered.stream().filter(o -> o.getRequiredDeliveryTime() != null && o.getActualDeliveryTime() != null
                && !o.getActualDeliveryTime().after(o.getRequiredDeliveryTime())).count();

        int overtimeCount = (int) delivered.stream().filter(o -> o.getRequiredDeliveryTime() != null && o.getActualDeliveryTime() != null
                && o.getActualDeliveryTime().after(o.getRequiredDeliveryTime())).count();

        BigDecimal onTimeRate = deliveredCount == 0 ? BigDecimal.ZERO : BigDecimal.valueOf(onTimeCount * 100.0 / deliveredCount).setScale(2, RoundingMode.HALF_UP);
        BigDecimal otifRate = deliveredCount == 0 ? BigDecimal.ZERO : BigDecimal.valueOf(
                delivered.stream().filter(o -> o.getRequiredDeliveryTime() != null && o.getActualDeliveryTime() != null
                        && !o.getActualDeliveryTime().after(o.getRequiredDeliveryTime())
                        && !Objects.equals(o.getExceptionFlag(), 1)).count() * 100.0 / deliveredCount
        ).setScale(2, RoundingMode.HALF_UP);

        // 平均完成耗时（小时）
        BigDecimal avgHours = BigDecimal.ZERO;
        if (!delivered.isEmpty()) {
            double sumHours = delivered.stream()
                    .filter(o -> o.getActualDeliveryTime() != null && o.getCreateTime() != null)
                    .mapToDouble(o -> (o.getActualDeliveryTime().getTime() - o.getCreateTime().getTime()) / 3600000.0)
                    .sum();
            int denom = (int) delivered.stream().filter(o -> o.getActualDeliveryTime() != null && o.getCreateTime() != null).count();
            avgHours = denom == 0 ? BigDecimal.ZERO : BigDecimal.valueOf(sumHours / denom).setScale(2, RoundingMode.HALF_UP);
        }

        DashboardSummaryVO vo = new DashboardSummaryVO();
        vo.setTotalKanban(total);
        vo.setCompletedKanban(completed);
        vo.setExceptionKanban(exception);
        vo.setCancelKanban(cancel);
        vo.setOnTimeRate(onTimeRate);
        vo.setAvgCompletionHours(avgHours);
        vo.setOvertimeCount(overtimeCount);
        vo.setOtifRate(otifRate);
        return vo;
    }

    @Override
    public List<DashboardTrendItemVO> getReceiverTrend(String granularity, Date startTime, Date endTime) {
        String g = Optional.ofNullable(granularity).map(String::toLowerCase).orElse("day");

        LambdaQueryWrapper<DeliveryOrder> w = new LambdaQueryWrapper<>();
        w.eq(DeliveryOrder::getDelFlag, 0);
        applyTimeRange(w, startTime, endTime);

        List<DeliveryOrder> list = this.list(w);

        DateTimeFormatter monthFmt = DateTimeFormatter.ofPattern("yyyy-MM");
        DateTimeFormatter dayFmt = DateTimeFormatter.ISO_DATE;
        WeekFields iso = WeekFields.ISO;

        Map<String, List<DeliveryOrder>> grouped = list.stream().collect(Collectors.groupingBy(o -> {
            Date d = o.getRequiredDeliveryTime();
            if (d == null) d = o.getCreateTime();
            if (d == null) return "unknown";
            LocalDate ld = d.toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            switch (g) {
                case "week":
                    int week = ld.get(iso.weekOfWeekBasedYear());
                    int year = ld.get(iso.weekBasedYear());
                    return String.format("%d-%02d", year, week);
                case "month":
                    return ld.format(monthFmt);
                case "day":
                default:
                    return ld.format(dayFmt);
            }
        }));

        List<DashboardTrendItemVO> items = new ArrayList<>();
        for (Map.Entry<String, List<DeliveryOrder>> e : grouped.entrySet()) {
            List<DeliveryOrder> orders = e.getValue();
            int total = orders.size();
            int completed = (int) orders.stream().filter(o -> Objects.equals(o.getStatus(), 30)).count();
            int exceptions = (int) orders.stream().filter(o -> Objects.equals(o.getExceptionFlag(), 1)).count();
            int deliveredCount = (int) orders.stream().filter(o -> Objects.equals(o.getStatus(), 30)).count();
            int onTimeCount = (int) orders.stream().filter(o -> Objects.equals(o.getStatus(), 30)
                    && o.getRequiredDeliveryTime() != null && o.getActualDeliveryTime() != null
                    && !o.getActualDeliveryTime().after(o.getRequiredDeliveryTime())).count();
            BigDecimal onTimeRate = deliveredCount == 0 ? BigDecimal.ZERO : BigDecimal.valueOf(onTimeCount * 100.0 / deliveredCount).setScale(2, RoundingMode.HALF_UP);

            DashboardTrendItemVO vo = new DashboardTrendItemVO();
            vo.setPeriod(e.getKey());
            vo.setTotal(total);
            vo.setCompleted(completed);
            vo.setExceptions(exceptions);
            vo.setOnTimeRate(onTimeRate);
            items.add(vo);
        }

        // 按 period 排序
        items.sort(Comparator.comparing(DashboardTrendItemVO::getPeriod));
        return items;
    }

    @Override
    public List<DashboardDistributionItemVO> getReceiverDistribution(Date startTime, Date endTime) {
        LambdaQueryWrapper<DeliveryOrder> w = new LambdaQueryWrapper<>();
        w.eq(DeliveryOrder::getDelFlag, 0);
        applyTimeRange(w, startTime, endTime);
        List<DeliveryOrder> list = this.list(w);

        // 以业务状态分布为例
        Map<String, Long> map = list.stream().collect(Collectors.groupingBy(o -> statusText(o.getStatus()), Collectors.counting()));
        return map.entrySet().stream()
                .map(e -> {
                    DashboardDistributionItemVO vo = new DashboardDistributionItemVO();
                    vo.setCategory(e.getKey());
                    vo.setCount(e.getValue().intValue());
                    return vo;
                })
                .sorted(Comparator.comparing(DashboardDistributionItemVO::getCategory))
                .collect(Collectors.toList());
    }

    @Override
    public List<DashboardRealtimeItemVO> getReceiverRealtime(Date startTime, Date endTime) {
        LambdaQueryWrapper<DeliveryOrder> w = new LambdaQueryWrapper<>();
        w.eq(DeliveryOrder::getDelFlag, 0);
        applyTimeRange(w, startTime, endTime);
        w.orderByDesc(DeliveryOrder::getCreateTime);
        List<DeliveryOrder> list = this.list(w);
        return list.stream().map(this::toRealtimeVO).collect(Collectors.toList());
    }

    @Override
    public List<DashboardExceptionItemVO> getReceiverExceptions(Date startTime, Date endTime) {
        LambdaQueryWrapper<DeliveryOrder> w = new LambdaQueryWrapper<>();
        w.eq(DeliveryOrder::getDelFlag, 0);
        w.eq(DeliveryOrder::getExceptionFlag, 1);
        applyTimeRange(w, startTime, endTime);
        List<DeliveryOrder> list = this.list(w);

        Map<Integer, Long> grouped = list.stream().collect(Collectors.groupingBy(o -> Optional.ofNullable(o.getExceptionType()).orElse(0), Collectors.counting()));
        return grouped.entrySet().stream()
                .map(e -> {
                    DashboardExceptionItemVO vo = new DashboardExceptionItemVO();
                    vo.setExceptionType(e.getKey() == null ? "" : String.valueOf(e.getKey()));
                    vo.setExceptionName(exceptionName(e.getKey()));
                    vo.setCount(e.getValue().intValue());
                    return vo;
                })
                .sorted(Comparator.comparing(DashboardExceptionItemVO::getExceptionType))
                .collect(Collectors.toList());
    }

    private DashboardRealtimeItemVO toRealtimeVO(DeliveryOrder o) {
        DashboardRealtimeItemVO vo = new DashboardRealtimeItemVO();
        vo.setKanbanCode(o.getKanbanCode());
        vo.setBusinessStatus(statusText(o.getStatus()));
        vo.setSenderWarehouse(o.getSenderWarehouse());
        vo.setReceiverWarehouse(o.getReceiverWarehouse());
        vo.setRequiredDeliveryTime(o.getRequiredDeliveryTime());
        vo.setPlannedDeliveryTime(o.getPlannedDeliveryTime());
        vo.setCreateTime(o.getCreateTime());
        return vo;
    }

    private String statusText(Integer status) {
        if (Objects.equals(status, 10)) return "草稿";
        if (Objects.equals(status, 20)) return "已发货";
        if (Objects.equals(status, 30)) return "已收货";
        if (Objects.equals(status, -10)) return "已作废";
        return "未知";
    }

    private String exceptionName(Integer type) {
        if (Objects.equals(type, 1)) return "数量差异";
        if (Objects.equals(type, 2)) return "质量问题";
        if (Objects.equals(type, 3)) return "时间延误";
        if (Objects.equals(type, 4)) return "其他";
        return "未归类";
    }
}